home *** CD-ROM | disk | FTP | other *** search
/ User's Choice Windows CD / User's Choice Windows CD (CMS Software)(1993).iso / win_q_t / spy30.zip / SPY.C < prev    next >
C/C++ Source or Header  |  1991-09-11  |  37KB  |  937 lines

  1. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *\
  2.  *  Spy.c                                                                    *
  3.  *  Windows Spy Program                                                      *
  4.  *  Public Domain                                                            *
  5.  *  Written by Michael Geary                                                 *
  6.  
  7.     Modified for Windows 3.0 by Dennis Chuah
  8.     chuah@stargate.elec.canterbury.ac.nz
  9.     chuahdc@stargate.elec.canterbury.ac.nz
  10.  
  11.       Modifications I made:
  12.     Minor modifications to this file
  13.        Changed a function (Paint) to use more the standard va_args
  14.     Deleted an include line in SPY.RC
  15.     Converted the Windows 2.0 icon file to 3.0 (and changed a few of
  16.        its colours as well).
  17.       What I did not do:
  18.     Made intensive tests on this port.
  19.     Guarantee that it would work on your machine.  (In fact I will
  20.        *NOT* even guarantee that you can compile it.)  This is a
  21.        10-minute hack so don't expect miracles.  I compiled it
  22.        using Borland C++, using the small model - all functions
  23.        exported.
  24.     Changed any of its algorithm.  Some of it may not work for
  25.        Windows 3.0.
  26.  
  27.       If you cannot run/compile this - tough.  I included the original
  28.       Windows 2.0 source so you can hack it if you like.
  29.  
  30.       Disclaimer:  By using this hack, you are accepting that I will
  31.     NOT be held responsible for any loss or damage resulting
  32.     from the use or misuse of this program.
  33.  
  34.       If you have a problem, you can either;
  35.       a)  Contact me.  I don't plan to support this program but try
  36.       me anyway - if I have the time and the knowledge, I will
  37.       try and help you.
  38.       b)  Contact the original author.  He is probably the person to
  39.       ask if you have a problem with the algorithm.  I merely
  40.       ported it to Windows 3.0.
  41.  
  42.       *** Happy hacking ***
  43.  
  44.  *  This program "spies" on all the windows that are currently open in your  *
  45.  *  Windows session, and displays a window containing all the information it *
  46.  *  can find out about those windows.  You can scroll through this window    *
  47.  *  using either the mouse or keyboard to view the information about the     *
  48.  *  various windows.  The "New Spy Mission" menu item re-captures the latest *
  49.  *  information.  This menu item is on the System menu so you can trigger it *
  50.  *  even if the Spy window is iconic.                                        *
  51. \* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  52.  
  53. #define LINT_ARGS
  54. #include <windows.h>
  55. #include <stdio.h>
  56. #include <stdarg.h>     /* Dennis: Added this include file */
  57. #include "spy.h"
  58.  
  59. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  60.  
  61. /*  The display for a single window looks like this in collapsed mode:
  62.  *
  63.  *  {Child|Popup|TopLevel} window HHHH {class} (L,T;R,B) "title"
  64.  *
  65.  *  or like this in expanded mode:
  66.  *
  67.  *      {Child|Popup|TopLevel} window handle: HHHH
  68.  *        Class name: {class name}
  69.  *        Window title: {title text}
  70.  *        Parent window handle: HHHH
  71.  *        Class function, window function: HHHH:HHHH, HHHH:HHHH
  72.  *        Class module handle, Window instance handle: HHHH, HHHH
  73.  *        Class extra alloc, Window extra alloc: DDDDD, DDDDD
  74.  *        Class style, Window style: HHHH, HHHHHHHH
  75.  *        Menu handle: HHHH   -or-  Control ID: DDDDD
  76.  *        Brush, Cursor, Icon handles: HHHH, HHHH, HHHH
  77.  *        Window rectangle: Left=DDDDD, Top=DDDDD, Right=DDDDD, Bottom=DDDDD
  78.  *        Client rectangle: Left=DDDDD, Top=DDDDD, Right=DDDDD, Bottom=DDDDD
  79.  *      {blank line}
  80.  *
  81.  *  Total number of lines for one window display: 13
  82.  */
  83.  
  84. #define LINES_PER_WINDOW    13
  85. #define WINDOW_WIDTH        120
  86.  
  87. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  88.  
  89. /*  The INFO structure contains all the information we gather up about each
  90.  *  window we are spying on.  We allocate an array of INFO structures in the
  91.  *  global heap, with one entry for each window in the system.
  92.  */
  93.  
  94. #define CLASSMAX    30
  95. #define TITLEMAX    50
  96.  
  97. typedef struct {
  98.     HWND    winHWnd;                /* Window handle */
  99.     char    winClass[CLASSMAX];     /* Class name */
  100.     HBRUSH  winBkgdBrush;           /* Background brush handle */
  101.     HCURSOR winCursor;              /* Cursor handle */
  102.     HICON   winIcon;                /* Icon handle */
  103.     HANDLE  winClassModule;         /* Module handle for owner of class */
  104.     WORD    winWndExtra;            /* Extra data allocated for each window */
  105.     WORD    winClsExtra;            /* Extra data allocated in class itself */
  106.     WORD    winClassStyle;          /* Class style word */
  107.     FARPROC winClassProc;           /* Window function declared for class */
  108.     HANDLE  winInstance;            /* Instance handle for window owner */
  109.     HWND    winHWndParent;          /* Parent window handle */
  110.     char    winTitle[TITLEMAX];     /* Window title or content string */
  111.     WORD    winControlID;           /* Control ID or menu handle */
  112.     FARPROC winWndProc;             /* Window function, usually = class fun. */
  113.     DWORD   winStyle;               /* Style doubleword for window (WS_...) */
  114.     RECT    winWindowRect;          /* Window rectangle (screen-relative) */
  115.     RECT    winClientRect;          /* Client rectangle within window rect. */
  116. } INFO;
  117.  
  118. typedef HANDLE      HINFO;          /* Handle to array of INFO structures */
  119. typedef INFO huge * LPINFO;         /* Far pointer to same when locked down */
  120.  
  121. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  122.  
  123. /*  The CsrScroll array is used for implementing keyboard scrolling.  By
  124.  *  looking up the keystroke in this array, we get the equivalent scroll
  125.  *  bar message.
  126.  */
  127.  
  128. #define VK_MIN_CURSOR  VK_PRIOR
  129. #define VK_MAX_CURSOR  VK_DOWN
  130.  
  131. struct {
  132.     char    csBar;      /* Which scroll bar this key is equivalent to */
  133.     char    csMsg;      /* The scroll message for this key */
  134. } CsrScroll[] = {
  135.     { SB_VERT, SB_PAGEUP   },  /* VK_PRIOR (PgUp)        */
  136.     { SB_VERT, SB_PAGEDOWN },  /* VK_NEXT  (PgDn)        */
  137.     { SB_VERT, SB_BOTTOM   },  /* VK_END   (End)         */
  138.     { SB_VERT, SB_TOP      },  /* VK_HOME  (Home)        */
  139.     { SB_HORZ, SB_LINEUP   },  /* VK_LEFT  (left arrow)  */
  140.     { SB_VERT, SB_LINEUP   },  /* VK_UP    (up arrow)    */
  141.     { SB_HORZ, SB_LINEDOWN },  /* VK_RIGHT (right arrow) */
  142.     { SB_VERT, SB_LINEDOWN }   /* VK_DOWN  (down arrow)  */
  143. };
  144.  
  145. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  146.  
  147. /*  Static variables
  148.  */
  149.  
  150. HANDLE      hInstance;              /* Our instance handle */
  151. HINFO       hInfo;                  /* Global handle to INFO array structure */
  152. LPINFO      lpInfo;                 /* Far pointer to INFO, when locked down */
  153. int         nWindows;               /* Total number of windows in system */
  154. DWORD       dwInfoSize;             /* Size of entire INFO array in bytes */
  155. FARPROC     lpprocCountWindow;      /* ProcInstance for CountWindow */
  156. FARPROC     lpprocSpyOnWindow;      /* ProcInstance for SpyOnWindow */
  157. BOOL        bInitted = FALSE;       /* TRUE when initialization completed */
  158. BOOL        bExpand = FALSE;        /* Expanded display mode? */
  159. BOOL        bUpdate = FALSE;
  160. int         nLinesPerWindow = 1;    /* 1 or LINES_PER_WINDOW */
  161. int         nCharSizeX;             /* Width of a character in pixels */
  162. int         nCharSizeY;             /* Height of a character in pixels */
  163. int         nExtLeading;            /* # pixels vertical space between chars */
  164. int         nPaintX;                /* For Paint function: X coordinate */
  165. int         nPaintY;                /* For Paint function: Y coordinate */
  166. HDC         hdcPaint;               /* For Paint function: hDC to paint into */
  167. char        szClass[10];            /* Our window class name */
  168. char        szTitle[40];            /* Our window title */
  169.  
  170. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  171.  
  172. /*  Declare full templates for all our functions.  This gives us strong type
  173.  *  checking on our functions.
  174.  */
  175.  
  176. BOOL    FAR PASCAL  CountWindow( HWND, long );
  177. int                 DoScrollMsg( HWND, int, WORD, int );
  178. void                HomeScrollBars( HWND, BOOL );
  179. HWND                Initialize( HANDLE, int );
  180.  
  181. /* Dennis: Changed this function */
  182. void        cdecl   Paint( char *, ...);
  183. void                PaintWindow( HWND );
  184. void                SetScrollBars( HWND );
  185. void                SetScrollBar1( HWND, int, int );
  186. BOOL                SpyOnAllWindows( HWND );
  187. BOOL    FAR PASCAL  SpyOnWindow( HWND, long );
  188. long    FAR PASCAL  SpyWndProc( HWND, unsigned, WORD, LONG );
  189. int         PASCAL  WinMain( HANDLE, HANDLE, LPSTR, int );
  190.  
  191. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  192.  
  193. /*  Enumeration function to count the number of windows in the system.  Called
  194.  *  once for each window, via EnumWindows and recursively via EnumChildWindows.
  195.  *  The lTopLevel parameter tells us which kind of call it is.
  196.  */
  197.  
  198. BOOL FAR PASCAL CountWindow( hWnd, lTopLevel )
  199.     HWND        hWnd;               /* Window handle for this window */
  200.     long        lTopLevel;          /* 1=top level window, 0=child window */
  201. {
  202.     /* Count the window */
  203.     dwInfoSize += sizeof(INFO);
  204.     ++nWindows;
  205.  
  206.     /* If this is a top level window (or popup), count its children */
  207.     if( lTopLevel )
  208.         EnumChildWindows( hWnd, lpprocCountWindow, 0L );
  209.  
  210.     return TRUE;  /* TRUE to continue enumeration */
  211. }
  212.  
  213. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  214.  
  215. /*  Process a scroll bar message.  Calculates the distance to scroll based on
  216.  *  the scroll bar range and the message code.  Limits the scroll to the actual
  217.  *  range of the scroll bar.  Sets the new scroll bar thumb position and
  218.  *  scrolls the window by the necessary amount.  Note that the scroll bar
  219.  *  ranges are set in terms of number of characters, while the window scrolling
  220.  *  is done by a number of pixels.  Returns the distance scrolled in chars.
  221.  */
  222.  
  223. int DoScrollMsg( hWnd, nBar, wCode, nThumb )
  224.     HWND        hWnd;               /* Window handle to scroll */
  225.     int         nBar;               /* Which scroll bar: SB_HORZ or SB_VERT */
  226.     WORD        wCode;              /* The scroll bar message code */
  227.     int         nThumb;             /* Thumb position for SB_THUMBPOSITION */
  228. {
  229.     int         nOld;               /* Previous scroll bar position */
  230.     int         nDiff;              /* Amount to change scroll bar by */
  231.     int         nMin;               /* Minimum value of scroll bar range */
  232.     int         nMax;               /* Maximum value of scroll bar range */
  233.     int         nPageSize;          /* Size of our window in characters */
  234.     RECT        rect;               /* Client rectangle for our window */
  235.  
  236.     /* Get old scroll position and scroll range */
  237.     nOld = GetScrollPos( hWnd, nBar );
  238.     GetScrollRange( hWnd, nBar, &nMin, &nMax );
  239.  
  240.     /* Quit if there is nowhere to scroll to (see SetScrollBars) */
  241.     if( nMax == MAXINT )
  242.         return 0;
  243.  
  244.     /* Calculate page size, horizontal or vertical as needed */
  245.     GetClientRect( hWnd, &rect );
  246.     if( nBar == SB_HORZ )
  247.         nPageSize = (rect.right - rect.left) / nCharSizeX;
  248.     else
  249.         nPageSize = (rect.bottom - rect.top) / nCharSizeY;
  250.  
  251.     /* Select the amount to scroll by, based on the scroll message */
  252.     switch( wCode ) {
  253.  
  254.         case SB_LINEUP:
  255.             nDiff = -1;
  256.             break;
  257.  
  258.         case SB_LINEDOWN:
  259.             nDiff = 1;
  260.             break;
  261.  
  262.         case SB_PAGEUP:
  263.             nDiff = -nPageSize;
  264.             break;
  265.  
  266.         case SB_PAGEDOWN:
  267.             nDiff = nPageSize;
  268.             break;
  269.  
  270.     case SB_THUMBPOSITION:
  271.             nDiff = nThumb - nOld;
  272.             break;
  273.  
  274.         case SB_TOP:
  275.             nDiff = -30000;  /* Kind of a kludge but it works... */
  276.             break;
  277.  
  278.         case SB_BOTTOM:
  279.             nDiff = 30000;
  280.             break;
  281.  
  282.         default:
  283.             return 0;
  284.     }
  285.  
  286.     /* Limit scroll destination to nMin..nMax */
  287.     if( nDiff < nMin - nOld )
  288.         nDiff = nMin - nOld;
  289.  
  290.     if( nDiff > nMax - nOld )
  291.         nDiff = nMax - nOld;
  292.  
  293.     if( nDiff == 0 )
  294.         return 0;  /* Return if net effect is nothing */
  295.  
  296.     /* OK, now we can set the new scroll bar position and scroll the window */
  297.     SetScrollPos( hWnd, nBar, nOld + nDiff, TRUE );
  298.  
  299.     ScrollWindow(
  300.         hWnd,
  301.         nBar == SB_HORZ ?  -nDiff*nCharSizeX : 0,
  302.         nBar == SB_HORZ ?  0 : -nDiff*nCharSizeY,
  303.         NULL,
  304.         NULL
  305.     );
  306.  
  307.     /* Force an immediate update for cleaner appearance */
  308.     UpdateWindow( hWnd );
  309.  
  310.     return nDiff;
  311. }
  312.  
  313. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  314.  
  315. /*  Set both scroll bars to the home position (0)
  316.  */
  317.  
  318. void HomeScrollBars( hWnd, bRedraw )
  319.     HWND        hWnd;               /* Window handle */
  320.     BOOL        bRedraw;            /* TRUE if scroll bars should be redrawn */
  321. {
  322.     SetScrollPos( hWnd, SB_HORZ, 0, bRedraw );
  323.     SetScrollPos( hWnd, SB_VERT, 0, bRedraw );
  324. }
  325.  
  326. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  327.  
  328. /*  Initialize the application.  Some of the initialization is different
  329.  *  depending on whether this is the first instance or a subsequent instance.
  330.  *  For example, we register our window class only in the first instance.
  331.  *  Returns TRUE if initialization succeeded, FALSE if failed.
  332.  */
  333.  
  334. HWND Initialize( hPrevInst, nCmdShow )
  335.     HANDLE      hPrevInst;          /* Previous instance handle, 0 if first */
  336.     int         nCmdShow;           /* Parameter from WinMain for ShowWindow */
  337. {
  338.     WNDCLASS    Class;              /* Class structure for RegisterClass */
  339.     HWND        hWnd;               /* Our window handle */
  340.     HDC         hDC;                /* Display context for our window */
  341.     TEXTMETRIC  Metrics;            /* Text metrics for System font */
  342.     HMENU       hMenu;              /* Menu handle of system menu */
  343.  
  344.     if( ! hPrevInst ) {
  345.         /* Initialization for first instance only */
  346.  
  347.         /* Load strings from resource file */
  348.         LoadString( hInstance, IDS_CLASS,    szClass,    sizeof(szClass) );
  349.         LoadString( hInstance, IDS_TITLE,    szTitle,    sizeof(szTitle) );
  350.  
  351.         /* Register our window class */
  352.         Class.style          = CS_HREDRAW | CS_VREDRAW;
  353.         Class.lpfnWndProc    = SpyWndProc;
  354.         Class.cbClsExtra     = 0;
  355.         Class.cbWndExtra     = 0;
  356.         Class.hInstance      = hInstance;
  357.         Class.hIcon          = LoadIcon( hInstance, szClass );
  358.         Class.hCursor        = LoadCursor( NULL, IDC_ARROW );
  359.         Class.hbrBackground  = COLOR_WINDOW + 1;
  360.         Class.lpszMenuName   = szClass;
  361.     Class.lpszClassName  = szClass;
  362.  
  363.         if( ! RegisterClass( &Class ) )
  364.             return FALSE;
  365.  
  366.     } else {
  367.         /* Initialization for subsequent instances only */
  368.  
  369.         /* Copy data from previous instance */
  370.     GetInstanceData( hPrevInst, szClass,    sizeof(szClass) );
  371.         GetInstanceData( hPrevInst, szTitle,    sizeof(szTitle) );
  372.     }
  373.  
  374.     /* Initialization for every instance */
  375.  
  376.     /* Set up ProcInstance pointers for our Enumerate functions */
  377.     lpprocCountWindow = MakeProcInstance( CountWindow, hInstance );
  378.     lpprocSpyOnWindow = MakeProcInstance( SpyOnWindow, hInstance );
  379.     if( ! lpprocCountWindow || ! lpprocSpyOnWindow )
  380.         return FALSE;
  381.  
  382.     /* Allocate our INFO structure with nothing really allocated yet */
  383.     hInfo = GlobalAlloc( GMEM_MOVEABLE, 1L );
  384.     if( ! hInfo )
  385.         return FALSE;
  386.  
  387.     /* Create our tiled window but don't display it yet */
  388. /* Dennis: changed this */
  389.     hWnd = CreateWindow(
  390.     szClass,                    /* Class name */
  391.     szTitle,                    /* Window title */
  392.     WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,  /* Window style */
  393.     CW_USEDEFAULT, CW_USEDEFAULT,
  394.     CW_USEDEFAULT, CW_USEDEFAULT,
  395.     NULL,                       /* Parent hWnd (none for top-level) */
  396.     NULL,                       /* Menu handle */
  397.     hInstance,                  /* Owning instance handle */
  398.     NULL                        /* Parameter to pass in WM_CREATE (none) */
  399.     );
  400.  
  401.     /* Initialize scroll bars - Windows doesn't do this for us */
  402.     HomeScrollBars( hWnd, FALSE );
  403.  
  404.     /* Calculate character size for system font */
  405.     hDC = GetDC( hWnd );
  406.     GetTextMetrics( hDC, &Metrics );
  407.     ReleaseDC( hWnd, hDC );
  408.     nExtLeading = Metrics.tmExternalLeading;
  409.     nCharSizeX = Metrics.tmMaxCharWidth;
  410.     nCharSizeY = Metrics.tmHeight + Metrics.tmExternalLeading;
  411.  
  412.     /* Make the window visible before grabbing spy info, so it's included */
  413.     ShowWindow( hWnd, nCmdShow );
  414.  
  415.     /* Now grab the spy information */
  416.     if( ! SpyOnAllWindows( hWnd ) )
  417.     return FALSE;
  418.  
  419.     if (!SetTimer (hWnd, 1, 5000, (FARPROC) NULL))
  420.        MessageBox (hWnd, "Cannot find a free system timer:\n"
  421.              "Display will not update automatically.",
  422.              "Window Spy:",
  423.              MB_OK | MB_APPLMODAL | MB_ICONINFORMATION);
  424.  
  425.     /* Got all the information, update our display */
  426.     UpdateWindow( hWnd );
  427.  
  428.     /* Make note that initialization is complete.  This is checked in our
  429.      * routine that handles WM_SIZE to eliminate some jitter on startup */
  430.     bInitted = TRUE;
  431.     return hWnd;
  432. }
  433.  
  434. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  435.  
  436. /*  Format and paint a line of text.  szFormat and Args are just as in a
  437.  *  sprintf() call (Args is a variable number of arguments).  The global
  438.  *  variables nPaintX and nPaintY tell where to paint the line.  We increment
  439.  *  nPaintY to the next line after painting.
  440.  *  Note the 'cdecl' declaration.  This forces this function to use the
  441.  *  standard C calling sequence, which is necessary with a variable number
  442.  *  of parameters.
  443.  */
  444.  
  445. /* Dennis: Changed this function to use the more standard va_args
  446.        construct.
  447. */
  448. void cdecl Paint(char *szFormat, ... )
  449.     /* Format string as used in printf() */
  450.     /* Zero or more parameters, as in printf */
  451. {
  452.     va_list argptr;
  453.     int         nLength;            /* Length of formatted string */
  454.     char        Buf[160];           /* Buffer to format string into */
  455.  
  456.     va_start (argptr, szFormat);
  457.     nLength = vsprintf( Buf, szFormat, argptr );
  458.     va_end(argptr);
  459.  
  460.     TextOut( hdcPaint, nPaintX, nPaintY+nExtLeading, Buf, nLength );
  461.     nPaintY += nCharSizeY;
  462. }
  463.  
  464. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  465.  
  466. /*  Paints our window or any portion of it that needs painting.
  467.  *  The BeginPaint call sets up a structure that tells us what rectangle of the
  468.  *  window to paint, along with other information for the painting process.
  469.  *  First, erase the background area if necessary.
  470.  *  Then, calculate the index into the INFO array to start with, based on the
  471.  *  painting rectangle and the scroll bar position, and lock down the INFO.
  472.  *  Finally, loop through the INFO array, painting the text for each entry.
  473.  *  Quit when we run out of entries or hit the bottom of the paint rectangle.
  474.  */
  475.  
  476. void PaintWindow( hWnd )
  477.     HWND        hWnd;               /* Window handle to paint */
  478. {
  479.     PAINTSTRUCT ps;                 /* Paint structure set up by BeginPaint */
  480.     DWORD       rgbOldTextColor;    /* Old text color (so we can restore it) */
  481.     DWORD       rgbOldBkColor;      /* Old background color */
  482.     int         nWin;               /* Index into INFO array */
  483.     int         X;                  /* X position for paint calculation */
  484.     int         Y;                  /* Y position for paint calculation */
  485.     PSTR        pTypeName;          /* Pointer to "Child", etc. string */
  486.  
  487.     /* Tell Windows we're painting, set up the paint structure. */
  488.     BeginPaint( hWnd, &ps );
  489.  
  490.     /* Store display context in global for Paint function */
  491.     /* Dennis:  This is probably not necessary again, since BeginPaint
  492.         returns the a handle to the window's client device
  493.         context, but as it is legal, I'll leave it alone.
  494.     */
  495.     hdcPaint = ps.hdc;
  496.  
  497.     /* Set up proper background and text colors and save old values */
  498.     rgbOldBkColor = SetBkColor( ps.hdc, GetSysColor( COLOR_WINDOW ) );
  499.     rgbOldTextColor = SetTextColor( ps.hdc, GetSysColor( COLOR_WINDOWTEXT ) );
  500.     /* Calculate horizontal paint position based on scroll bar position */
  501.     X = ( 1 - GetScrollPos( hWnd, SB_HORZ ) ) * nCharSizeX;
  502.  
  503.     /* Calculate index into INFO array and vertical paint position, based on
  504.      * scroll bar position and top of painting rectangle */
  505.     Y = GetScrollPos( hWnd, SB_VERT );
  506.     nWin = ( ps.rcPaint.top / nCharSizeY + Y ) / nLinesPerWindow;
  507.     nPaintY = ( nWin * nLinesPerWindow - Y ) * nCharSizeY;
  508.  
  509.     /* Lock down INFO array and set lpInfo pointing to first entry to paint */
  510.     lpInfo = (LPINFO)GlobalLock( hInfo );
  511.     lpInfo += nWin;
  512.  
  513.     /* Loop through INFO entries, painting each one until we run out of entries
  514.      * or until we are past the bottom of the paint rectangle.  We don't worry
  515.      * much about painting outside the rectangle - Windows will clip for us. */
  516.     while( nWin < nWindows  &&  nPaintY < ps.rcPaint.bottom )
  517.     {
  518.     /* Set X position and indent child windows, also set up pTypeName */
  519.     nPaintX = X;
  520.     if( lpInfo->winStyle & WS_CHILD ) {
  521.         nPaintX += nCharSizeX * ( bExpand ? 4 : 2 );
  522.         pTypeName = "Child";
  523.     } else if( lpInfo->winStyle & WS_ICONIC ) {
  524.         pTypeName = "Icon ";
  525.     } else if( lpInfo->winStyle & WS_POPUP ) {
  526.         pTypeName = "Popup";
  527.     } else {
  528.         pTypeName = "Top Level";
  529.     }
  530.  
  531.     if( ! bExpand ) {
  532.  
  533.         /* Paint the one-liner */
  534.         Paint(
  535.         "%s window %04X {%Fs} (%d,%d;%d,%d) \"%Fs\"",
  536.         pTypeName,
  537.         lpInfo->winHWnd,
  538.         lpInfo->winClass,
  539.         lpInfo->winWindowRect.left,
  540.         lpInfo->winWindowRect.top,
  541.         lpInfo->winWindowRect.right,
  542.         lpInfo->winWindowRect.bottom,
  543.         lpInfo->winTitle
  544.         );
  545.  
  546.     } else {
  547.  
  548.         /* Paint the expanded form, first the window handle */
  549.         Paint(
  550.         "%s window handle: %04X",
  551.         pTypeName,
  552.         lpInfo->winHWnd
  553.         );
  554.  
  555.         /* Paint the rest of the info, indented two spaces farther over */
  556.         nPaintX += nCharSizeX * 2;
  557.  
  558.         Paint( "Class name: %Fs", lpInfo->winClass );
  559.         Paint( "Window title: %Fs", lpInfo->winTitle );
  560.         Paint( "Parent window handle: %04X", lpInfo->winHWndParent );
  561.         Paint(
  562.         "Class function, Window function: %p, %p",
  563.         lpInfo->winClassProc,
  564.         lpInfo->winWndProc
  565.         );
  566.         Paint(
  567.                 "Class module handle, Window instance handle: %04X, %04X",
  568.                 lpInfo->winClassModule,
  569.                 lpInfo->winInstance
  570.             );
  571.             Paint(
  572.                 "Class extra alloc, Window extra alloc: %d, %d",
  573.                 lpInfo->winClsExtra,
  574.                 lpInfo->winWndExtra
  575.             );
  576.             Paint(
  577.                 "Class style, Window style: %04X, %08lX",
  578.                 lpInfo->winClassStyle,
  579.                 lpInfo->winStyle
  580.             );
  581.             Paint(
  582.                 lpInfo->winStyle & WS_CHILD ?  "Control ID: %d" :
  583.                                                "Menu handle: %04X",
  584.                 lpInfo->winControlID
  585.             );
  586.             Paint(
  587.                 "Brush, Cursor, Icon handles: %04X, %04X, %04X",
  588.                 lpInfo->winBkgdBrush,
  589.                 lpInfo->winCursor,
  590.                 lpInfo->winIcon
  591.             );
  592.             Paint(
  593.                 "Window rectangle: Left=%4d, Top=%4d, Right=%4d, Bottom=%4d",
  594.                 lpInfo->winWindowRect.left,
  595.                 lpInfo->winWindowRect.top,
  596.                 lpInfo->winWindowRect.right,
  597.                 lpInfo->winWindowRect.bottom
  598.             );
  599.             Paint(
  600.                 "Client rectangle: Left=%4d, Top=%4d, Right=%4d, Bottom=%4d",
  601.         lpInfo->winClientRect.left,
  602.                 lpInfo->winClientRect.top,
  603.                 lpInfo->winClientRect.right,
  604.                 lpInfo->winClientRect.bottom
  605.             );
  606.  
  607.             /* Make a blank line - it's already erased, so just increment Y */
  608.             nPaintY += nCharSizeY;
  609.         }
  610.  
  611.         /* Increment to next INFO entry */
  612.         ++nWin;
  613.         ++lpInfo;
  614.     }
  615.  
  616.     /* Unlock the INFO array */
  617.     GlobalUnlock( hInfo );
  618.  
  619.     /* Restore old colors */
  620.     SetBkColor( ps.hdc, rgbOldBkColor );
  621.     SetTextColor( ps.hdc, rgbOldTextColor );
  622.  
  623.     /* Tell Windows we're done painting */
  624.     EndPaint( hWnd, &ps );
  625. }
  626.  
  627. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  628.  
  629. /*  Set horizontal and vertical scroll bars, based on the window size and the
  630.  *  number of INFO entries.  The scroll bar ranges are set to give a total
  631.  *  width of WINDOW_WIDTH and a total height equal to the number of lines of
  632.  *  information available.  For example, if there are 130 lines of information
  633.  *  and the window height is 10 characters, the vertical scroll range is set
  634.  *  to 120 (130-10).  This lets you scroll through everything and still have
  635.  *  a full window of information at the bottom.  (Unlike, say, Windows Write,
  636.  *  where if you scroll to the bottom you have a blank screen.)
  637.  */
  638.  
  639. void SetScrollBars( hWnd )
  640.     HWND        hWnd;               /* Window handle */
  641. {
  642.     RECT        rect;               /* The window's client rectangle */
  643.  
  644.     GetClientRect( hWnd, &rect );
  645.  
  646.     SetScrollBar1(
  647.         hWnd, SB_HORZ,
  648.         WINDOW_WIDTH - rect.right / nCharSizeX
  649.     );
  650.  
  651.     SetScrollBar1(
  652.         hWnd, SB_VERT,
  653.         nWindows * nLinesPerWindow - rect.bottom / nCharSizeY
  654.     );
  655. }
  656.  
  657. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  658.  
  659. /*  Set one scroll bar's maximum range.  We always set the minimum to zero,
  660.  *  although Windows allows other values.  There is one case we handle
  661.  *  specially.  If you set a scroll bar range to minimum==maximum (maximum =
  662.  *  zero for us), Windows does not actually set the range, but instead turns
  663.  *  off the scroll bar completely, changing the window style by turning off
  664.  *  the WS_HSCROLL or WS_VSCROLL bit.  For example, this is how the MS-DOS
  665.  *  Executive makes its scroll bars appear and disappear.  This behavior is
  666.  *  fine if you take it into account in your programming in two ways.  First,
  667.  *  whenever you do a GetScrollRange you must first check the window style to
  668.  *  see if that scroll bar still exists, because you will *not* get the correct
  669.  *  answer from GetScrollRange if it has been removed.  Second, you must be
  670.  *  prepared to get some extra WM_SIZE messages, because your client area
  671.  *  changes size when the scroll bars appear and disappear.  This can cause
  672.  *  some sloppy looking screen painting.  We take a different approach, always
  673.  *  keeping the scroll bars visible.  If the scroll bar range needs to be set
  674.  *  to zero, instead we set it to MAXINT so the bar remains visible.  Then, in
  675.  *  DoScrollMessage we check for this case and return without scrolling.
  676.  */
  677.  
  678. void SetScrollBar1( hWnd, nBar, nMax )
  679.     HWND         hWnd;              /* Window handle */
  680.     int          nBar;              /* Which scroll bar: SB_HORZ or SB_VERT */
  681.     int          nMax;              /* Value to set maximum range to */
  682. {
  683.     int          nOldMin;           /* Previous minimum value (always 0) */
  684.     int          nOldMax;           /* Previous maximum value */
  685.  
  686.     /* Check for a negative or zero range and set our special case flag.
  687.      * Also, set the thumb position to zero in this case. */
  688.     if( nMax <= 0 ) {
  689.         nMax = MAXINT;
  690.         DoScrollMsg( hWnd, nBar, SB_THUMBPOSITION, 0 );
  691.     }
  692.  
  693.     /* Find out the previous range, and set it if it has changed */
  694.     GetScrollRange( hWnd, nBar, &nOldMin, &nOldMax );
  695.     if( nMax != nOldMax )
  696.         SetScrollRange( hWnd, nBar, 0, nMax, TRUE );
  697. }
  698.  
  699. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  700.  
  701. /*  Loop through all windows in the system and gather up information for the
  702.  *  INFO structure for each.  Use the EnumWindows and EnumChildWindows
  703.  *  functions to loop through them.  We actually loop through them twice:
  704.  *  first, to simply count them so we can allocate global memory for the
  705.  *  INFO structure, and again to actually fill in the structure.  After
  706.  *  gathering up the information, we invalidate our window, which will cause
  707.  *  a WM_PAINT message to be posted, so it will get repainted.
  708.  */
  709.  
  710. BOOL SpyOnAllWindows( hWnd )
  711.     HWND        hWnd;               /* Window handle */
  712. {
  713.     /* Calculate the number of windows and amount of memory needed */
  714.     nWindows = 0;
  715.     dwInfoSize = 0;
  716.     EnumWindows( lpprocCountWindow, 1L );
  717.  
  718.     /* Allocate the memory, complain if we couldn't get it */
  719.     hInfo = GlobalReAlloc( hInfo, dwInfoSize, GMEM_MOVEABLE );
  720.     if( ! hInfo ) {
  721.         nWindows = 0;
  722.         dwInfoSize = 0;
  723.         GlobalDiscard( hInfo );
  724.         MessageBox(
  725.             GetActiveWindow(),
  726.             "Insufficient memory!!",
  727.             NULL,
  728.             MB_OK | MB_ICONHAND
  729.         );
  730.         PostQuitMessage( 0 );
  731.         return FALSE;
  732.     }
  733.  
  734.     /* Lock down the memory and fill in the information, then unlock it */
  735.     lpInfo = (LPINFO)GlobalLock( hInfo );
  736.     EnumWindows( lpprocSpyOnWindow, 1L );
  737.     GlobalUnlock( hInfo );
  738.  
  739.     /* Set the scroll bars based on new window count, repaint our window */
  740. //    SetScrollBars( hWnd );
  741. //    HomeScrollBars( hWnd, TRUE );
  742.     InvalidateRect( hWnd, NULL, TRUE );
  743.  
  744.     return TRUE;
  745. }
  746.  
  747. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  748.  
  749. /*  Enumeration function to gather up the information for a single window and
  750.  *  store it in the INFO array entry pointed to by lpInfo.  Increment lpInfo
  751.  *  to the next entry afterward.  Called once for each window, via EnumWindows
  752.  *  for each top level and popup window, and recursively via EnumChildWindows
  753.  *  for child windows.  The lTopLevel parameter tells which kind of call it is.
  754.  */
  755.  
  756. BOOL FAR PASCAL SpyOnWindow( hWnd, lTopLevel )
  757.     HWND        hWnd;               /* Window handle */
  758.     long        lTopLevel;          /* 1=top level window, 0=child window */
  759. {
  760.     /* Gather up this window's information */
  761.     lpInfo->winHWnd = hWnd;
  762.     GetClassName( hWnd, lpInfo->winClass, CLASSMAX );
  763.     lpInfo->winClass[ CLASSMAX - 1 ] = 0;
  764.     lpInfo->winInstance = GetWindowWord( hWnd, GWW_HINSTANCE );
  765.     lpInfo->winHWndParent = GetParent( hWnd );
  766.     GetWindowText( hWnd, lpInfo->winTitle, TITLEMAX );
  767.     lpInfo->winTitle[ TITLEMAX - 1 ] = 0;
  768.     lpInfo->winControlID = GetWindowWord( hWnd, GWW_ID );
  769.     lpInfo->winWndProc = (FARPROC)GetWindowLong( hWnd, GWL_WNDPROC );
  770.     lpInfo->winStyle = GetWindowLong( hWnd, GWL_STYLE );
  771.     GetClientRect( hWnd, &lpInfo->winClientRect );
  772.     GetWindowRect( hWnd, &lpInfo->winWindowRect );
  773.  
  774.     /* Gather up class information */
  775.     lpInfo->winBkgdBrush = GetClassWord( hWnd, GCW_HBRBACKGROUND );
  776.     lpInfo->winCursor = GetClassWord( hWnd, GCW_HCURSOR );
  777.     lpInfo->winIcon = GetClassWord( hWnd, GCW_HICON );
  778.     lpInfo->winClassModule = GetClassWord( hWnd, GCW_HMODULE );
  779.     lpInfo->winWndExtra = GetClassWord( hWnd, GCW_CBWNDEXTRA );
  780.     lpInfo->winClsExtra = GetClassWord( hWnd, GCW_CBCLSEXTRA );
  781.     lpInfo->winClassStyle = GetClassWord( hWnd, GCW_STYLE );
  782.     lpInfo->winClassProc = (FARPROC)GetClassLong( hWnd, GCL_WNDPROC );
  783.  
  784.     /* Move on to next entry in table */
  785.     ++lpInfo;
  786.  
  787.     /* If it's a top level window, get its children too */
  788.     if( lTopLevel )
  789.         EnumChildWindows( hWnd, lpprocSpyOnWindow, 0L );
  790.  
  791.     return TRUE;  /* TRUE to continue enumeration */
  792. }
  793.  
  794. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  795.  
  796. /*  Window function for our main window.  All messages for our window are sent
  797.  *  to this function.  For messages that we do not handle here, we call
  798.  *  DefWindowProc, which performs Windows' default processing for a message.
  799.  */
  800.  
  801. long FAR PASCAL SpyWndProc( hWnd, wMsg, wParam, lParam )
  802.     HWND        hWnd;               /* Window handle */
  803.     unsigned    wMsg;               /* Message number */
  804.     WORD        wParam;             /* Word parameter for the message */
  805.     LONG        lParam;             /* Long parameter for the message */
  806. {
  807.     RECT        rect;               /* A rectangle */
  808.  
  809.     switch( wMsg ) {
  810.  
  811.         /* Menu command message - process the command */
  812.         case WM_COMMAND:
  813.             if( LOWORD(lParam) )
  814.         break;  /* not a command */
  815.         switch( wParam ) {
  816.         case CMD_UPDATE:
  817.             bUpdate = !bUpdate;
  818.             CheckMenuItem (GetMenu (hWnd), CMD_UPDATE,
  819.                MF_BYCOMMAND | (bUpdate ? MF_CHECKED : MF_UNCHECKED));
  820.             return 0;
  821.         case CMD_EXPAND:
  822.             bExpand = ! bExpand;
  823.             nLinesPerWindow = ( bExpand ? LINES_PER_WINDOW : 1 );
  824.             CheckMenuItem(
  825.             GetMenu( hWnd ),
  826.             CMD_EXPAND,
  827.             bExpand ? MF_CHECKED : MF_UNCHECKED
  828.             );
  829.             InvalidateRect( hWnd, NULL, TRUE );
  830.             HomeScrollBars( hWnd, FALSE );
  831.             SetScrollBars( hWnd );
  832.             return 0L;
  833.         case CMD_SPY:
  834.             SpyOnAllWindows( hWnd );
  835.             return 0L;
  836.         case CMD_EXIT:
  837.             PostMessage (hWnd, WM_CLOSE, 0, 0L);
  838.             return 0;
  839.         default:
  840.             break;
  841.         }
  842.         break;
  843.  
  844. /* Dennis: Added this line to interface with the timer */
  845.     case WM_TIMER:
  846.        if (!bUpdate) break;
  847.        SpyOnAllWindows (hWnd);
  848.        return 0;
  849.  
  850.     /* Destroy-window message - time to quit the application */
  851.     case WM_DESTROY:
  852.         PostQuitMessage( 0 );
  853.         return 0L;
  854.  
  855.     /* Horizontal scroll message - scroll the window */
  856.     case WM_HSCROLL:
  857.         DoScrollMsg( hWnd, SB_HORZ, wParam, (int)lParam );
  858.         return 0L;
  859.  
  860.     /* Key-down message - handle cursor keys, ignore other keys */
  861.     case WM_KEYDOWN:
  862.         if( wParam >= VK_MIN_CURSOR  &&  wParam <= VK_MAX_CURSOR ) {
  863.         DoScrollMsg(
  864.             hWnd,
  865.             CsrScroll[ wParam - VK_MIN_CURSOR ].csBar,
  866.             CsrScroll[ wParam - VK_MIN_CURSOR ].csMsg,
  867.             0
  868.         );
  869.         }
  870.         return 0L;
  871.  
  872.     /* Paint message - repaint all or part of our window */
  873.     case WM_PAINT:
  874.         PaintWindow( hWnd );
  875.         return 0L;
  876.  
  877.     /* Size message - recalculate our scroll bars to take the new size
  878.      * into account, but only if initialization has been completed.  There
  879.      * are several superfluous WM_SIZE messages sent during initialization,
  880.          * and it looks ugly if we repaint the scroll bars for all these. */
  881.         case WM_SIZE:
  882.             if( bInitted )
  883.                 SetScrollBars( hWnd );
  884.             return 0L;
  885.  
  886.         /* Vertical scroll message - scroll the window */
  887.         case WM_VSCROLL:
  888.             DoScrollMsg( hWnd, SB_VERT, wParam, (int)lParam );
  889.             return 0L;
  890.  
  891.         /* For all other messages, we pass them on to DefWindowProc */
  892.         default:
  893.             break;
  894.     }
  895.     return DefWindowProc( hWnd, wMsg, wParam, lParam );
  896. }
  897.  
  898. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  899.  
  900. /*  Application main program.  Not much is done here - we just initialize
  901.  *  the application, putting up our window, and then we go into the typical
  902.  *  message dispatching loop that every Windows application has.
  903.  */
  904.  
  905. /* Dennis: Added this pragma to avoid the Parameter is never used warning
  906. */
  907. #pragma argsused
  908. int PASCAL WinMain( hInst, hPrevInst, lpszCmdLine, nCmdShow )
  909.     HANDLE      hInst;              /* Our instance handle */
  910.     HANDLE      hPrevInst;          /* Previous instance of this application */
  911.     LPSTR       lpszCmdLine;        /* Pointer to any command line params */
  912.     int         nCmdShow;           /* Parameter to use for first ShowWindow */
  913. {
  914.     MSG         msg;                /* Message structure */
  915.     HWND    hWnd;
  916.  
  917.     /* Save our instance handle in static variable */
  918.     hInstance = hInst;
  919.  
  920.     /* Initialize application, quit if any errors */
  921.     if( ! (hWnd = Initialize( hPrevInst, nCmdShow)) )
  922.     return 1;
  923.  
  924.     /* Main message processing loop.  Get each message, then translate keyboard
  925.      * messages, and finally dispatch each message to its window function. */
  926.     while( GetMessage( &msg, NULL, 0, 0 ) ) {
  927.     TranslateMessage( &msg );
  928.     DispatchMessage( &msg );
  929.     }
  930.  
  931.     KillTimer (hWnd, 1);
  932.  
  933.     return msg.wParam;
  934. }
  935.  
  936. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  937.